package ch.simonste.jasscam;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;

import android.util.Log;

public class CardsDetector {
	private static final Scalar opencvRED = new Scalar(0xff, 0x0, 0x0, 0xff);
	private static final Scalar opencvBLUE = new Scalar(0x00, 0x0, 0xff, 0xff);
	private static final Scalar opencvGREEN = new Scalar(0x00, 0xff, 0x0, 0xff);
	private static final Scalar opencvYellow = new Scalar(0xff, 0xff, 0x0, 0xff);
	
	private Mat mProcessed = new Mat();
	private Mat mGray = new Mat();
	private List<MatOfPoint> mSquares = new ArrayList<MatOfPoint>();
	private List<MatOfPoint> mShapes = new ArrayList<MatOfPoint>();

	private double threshold = 60;
	
	public void process(Mat img)
	{
		img.copyTo(mProcessed);
		//Imgproc.cvtColor(img, mProcessed, Imgproc.COLOR_RGBA2GRAY);		
		Imgproc.cvtColor(img, mGray, Imgproc.COLOR_RGBA2GRAY);
				
		//edges.copyTo(mProcessed);
		getRectangles();
		Core.polylines(mProcessed, mShapes, true, opencvBLUE, 2);
		Core.polylines(mProcessed, mSquares, true, opencvRED, 2);
	}
	
	public Mat getProcessed(){
		return mProcessed;
	}
	
	private void getRectangles(){
		mSquares.clear();
		mShapes.clear();
    	
		Mat edges = new Mat();
    	Imgproc.Canny(mGray, edges, threshold, threshold*3);
        Imgproc.dilate(edges, edges, new Mat());
        
//        List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
//     	Imgproc.findContours(edges, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
     	
//     Core.polylines(mProcessed, contours, true, opencvYellow, 1);
    	     	
     	Mat lines = new Mat();
     	//Imgproc.HoughLines(edges, lines, 2, 2*Math.PI/180, 250);
     	Imgproc.HoughLinesP(edges, lines, 2, 2*Math.PI/180, 250);
     	
        for (int i = 0; i < lines.cols() ; i++) 
        {
        	double[] vec = lines.get(0, i);
        	Point p1 = new Point(vec[0], vec[1]);
        	Point p2 = new Point(vec[2], vec[3]);
        	
        	if( getDistance(p1, p2) > 100 ){
        		Core.line(mProcessed, new Point(vec[0],vec[1]), new Point(vec[2],vec[3]), opencvGREEN, 2);
        	}
//        	double rho = vec[0];
//        	double theta = vec[1];
//        	
//        	double a = Math.cos(theta), b = Math.sin(theta);
//        	double x0 = a*rho, y0 = b*rho;
//        	
//        	Point pt1 = new Point();
//        	Point pt2 = new Point();       	    	  
//        	pt1.x = Math.round(x0 + 1000*(-b));
//        	pt1.y = Math.round(y0 + 1000*(a));
//        	pt2.x = Math.round(x0 - 1000*(-b));
//        	pt2.y = Math.round(y0 - 1000*(a));
//            Core.line(mProcessed, pt1, pt2, opencvGREEN, 2);
            

        }

     /*
     	for( MatOfPoint contour : contours){
         	MatOfPoint2f approxCurve = new MatOfPoint2f();
     		Imgproc.approxPolyDP(new MatOfPoint2f( contour.toArray()), approxCurve, 10, true);	//    Imgproc.arcLength(List<Point>, true)
     		
     		if( approxCurve.rows() < 6 && Math.abs( Imgproc.contourArea( approxCurve ) ) > 2000.0 ){
     			mShapes.add(new MatOfPoint(approxCurve.toArray()));
     		}
     		
//     		if( isSquare(approxCurve) ){
//     			mSquares.add(new MatOfPoint( approxCurve.toArray() ));
//     		}
     	}   
     	*/	
	}
	
	private boolean isSquare(MatOfPoint2f approxCurve){
 		if( approxCurve.rows() == 4 ){
 			if( Math.abs( Imgproc.contourArea( approxCurve ) ) > 2000.0 ){
     			List<Point> Points = Arrays.asList(
     					new Point( approxCurve.get(0,0)[0] ,approxCurve.get(0,0)[1] ),
     					new Point( approxCurve.get(1,0)[0] ,approxCurve.get(1,0)[1] ),
     					new Point( approxCurve.get(2,0)[0] ,approxCurve.get(2,0)[1] ),
     					new Point( approxCurve.get(3,0)[0] ,approxCurve.get(3,0)[1] ));
     			    	

     			double l1 = getDistance( Points.get(0), Points.get(1) );
     			double l2 = getDistance( Points.get(1), Points.get(2) );
     			double l3 = getDistance( Points.get(2), Points.get(3) );
     			double l4 = getDistance( Points.get(3), Points.get(0) );

     			// Orientation
     			int[] k = {0,1,2,3};
     			if( l2+l4 > l1+l3 ){
     				k[0] = 1; k[1] = 2; k[2] = 3; k[3] = 0;
     			}

     			boolean clockwise = isClockwise(Points.get(0), Points.get(1), Points.get(2));
     			if( clockwise ){
     				int[] l = {k[3], k[2], k[1], k[0]};
     				k = l;
     			}
     			
     			// Parallel
     			double thres = 0.1;  	
     			boolean parallel = Math.abs( l1/(l1+l3) - 0.5) < thres && Math.abs( l2/(l2+l4) - 0.5 ) < thres;
     			if( parallel ){
     				return true;
     			}
 			}
		}
 		return false;
	}
		
    private double getDistance(Point p1, Point p2){
    	return Math.sqrt( Math.pow(p2.x - p1.x,2) + Math.pow(p2.y - p1.y,2) );   	
    }
    
    private boolean isClockwise(Point p1, Point p2, Point p3){
    	Point v1 = new Point( p1.x-p2.x, p1.y-p2.y );
    	Point v2 = new Point( p3.x-p2.x, p3.y-p2.y );
    	
    	double z = v1.x*v2.y - v1.y*v2.x;
    	if ( z > 0 ) return true;
    	
    	return false;
    }
}
